using System;
using System.Collections.Generic;
using System.Text;
using SlimDX;

namespace Barrage
{
    /// <summary>
    /// Represents a motion matrix for elements of the engine
    /// </summary>
    public class Motion
    {
        Matrix parent = SlimDX.Matrix.Identity;
        /// <summary>
        /// Gets/Sets the parent matrix. Defaults to identity matrix
        /// </summary>
        public Matrix Parent { get { return parent; } set { parent = value; } }

        bool updated = false;

        /// <summary>
        /// Returns the world matrix (this motion matrix multiplied by its parent matrix)
        /// </summary>
        public Matrix WorldMatrix{get{return Matrix*parent;}}
        /// <summary>
        /// Gets the translation vector of the world matrix
        /// </summary>
        public Vector3 WorldTranslation
        {
            get
            {
                Vector3 scale;
                Quaternion rotation;
                Vector3 translation;
                if (WorldMatrix.Decompose(out scale,out rotation,out translation))
                {
                    return translation;
                }
                return Vector3.Zero;
            }
        }

        Matrix matrix= SlimDX.Matrix.Identity;
        /// <summary>
        /// Gets / Sets the motion matrix
        /// </summary>
        public Matrix Matrix { get { if (!updated) updateMatrix(); return matrix; } set { matrix = value; updated = true; DecomposeMatrix(); } }
        Vector3 scale = new Vector3(1,1,1);
        /// <summary>
        /// Gets/Sets the scale component of the matrix
        /// </summary>
        public Vector3 Scale { get { return scale; } set { scale = value; updated = false; } }
        Vector3 scaleCenter = new Vector3();
        /// <summary>
        /// Gets/Sets the scale center component of the matrix
        /// </summary>
        public Vector3 ScaleCenter { get { return scaleCenter; } }
        Vector3 rotationCenter = new Vector3();
        /// <summary>
        /// Gets/Sets the rotation center component of the matrix
        /// </summary>
        public Vector3 RotationCenter { get { return rotationCenter; } }
        Vector3 translation = new Vector3();
        /// <summary>
        /// Gets/Sets the translation component of the matrix
        /// </summary>
        public Vector3 Translation { get { return translation; } set { translation = value; updated = false; } }
        Quaternion scaleRotation = new Quaternion();
        /// <summary>
        /// Gets/Sets the scale rotation component of the matrix
        /// </summary>
        public Quaternion ScaleRotation { get { return scaleRotation; } }
        Quaternion rotation = Quaternion.Identity;
        /// <summary>
        /// Gets/Sets the rotation component of the matrix
        /// </summary>
        public Quaternion Rotation { get { return rotation; } set { rotation = value; updated = false; } }

        /// <summary>
        /// Gets the transformation of the Y vector by this motion's rotation component
        /// </summary>
        public Vector3 YVector { get { Vector4 v=Vector3.Transform(Vector3.UnitY, rotation); return new Vector3(v.X,v.Y,v.Z); } }
        /// <summary>
        /// Gets the transformation of the Z vector by this motion's rotation component
        /// </summary>
        public Vector3 ZVector { get { Vector4 v = Vector3.Transform(Vector3.UnitZ, rotation); return new Vector3(v.X, v.Y, v.Z); } }

        /// <summary>
        /// Decomposes the matrix in scale, rotation and translation components. Only useful if the Matrix property has been set directly.
        /// </summary>
        public void DecomposeMatrix()
        {
            Vector3 scaleVector;
            Quaternion rotationQuaternion;
            Vector3 translationVector;
            if (matrix.Decompose(out scaleVector, out rotationQuaternion, out translationVector))
            {
                scale = scaleVector;
                rotation = rotationQuaternion;
                translation = translationVector;
                scaleCenter = Vector3.Zero;
                rotationCenter = Vector3.Zero;
                scaleRotation = Quaternion.Identity;
            }
        }

        /// <summary>
        /// Makes this motion components identical to the motion passed.
        /// Since the '=' operator only changes the instance reference, this method must be used.
        /// </summary>
        /// <param name="motion"></param>
        public void Assign(Motion motion)
        {
            this.parent = motion.parent;
            this.matrix = motion.matrix;
            this.rotation = motion.rotation;
            this.rotationCenter = motion.rotationCenter;
            this.scale = motion.scale;
            this.scaleCenter = motion.scaleCenter;
            this.scaleRotation = motion.scaleRotation;
            this.translation = motion.translation;
            this.updated = motion.updated;
        }

        /// <summary>
        /// Creates a motion with the identity matrix
        /// </summary>
        public Motion()
        {
            
        }

        /// <summary>
        /// Recreates the matrix from translation,scale,rotation, etc. values
        /// </summary>
        protected void updateMatrix(){
            matrix = SlimDX.Matrix.Transformation(scaleCenter, scaleRotation, scale, rotationCenter , rotation, translation);
            updated = true;
        }

        /// <summary>
        /// Recreates matrix if any of the components of the motion changed
        /// </summary>
        public void Update()
        {
            if (!updated)
            updateMatrix();

        }

        /// <summary>
        /// Implicit cast to Matrix
        /// </summary>
        /// <param name="motion">Motion instance</param>
        /// <returns>The motion's WorldMatrix value</returns>
        public static implicit operator Matrix(Motion motion)
        {
            return motion.WorldMatrix;
        }
    }
}
